<?php
/*
hydratag search engine
TODO -- possibly implement a level higher to do map and reduce etc
eg: defining add, expecting relateObject
also perhaps add could be used with no tags -- instead derived from object
perhaps having this method, allowing it to be overridden

This is depending on the implementation of the other trackers: couchdb, sqlite
etc
*/

abstract class indexer
{
	public static $max_results = 100;

	// optionally give a name to a partictular index
	abstract public function __construct($namespace = null);
	// add an object to the index, associating an array of tags with it
	// Note that it is preferred, but not required, that the indexer
	// backends store the data in their native formats.
	abstract protected function insert($object, array $tags);
	// Return an array of objects that have $tags in common
	abstract protected function query(array $tags);
	// Empty the index. This is typically used before a new absolute crawl.
	abstract public function flush();

	// add an object to the index, optionally explicity defining the
	// (arbtrary) metadata to associate with it. If the metadata is not
	// supplied, it will be derived from the object itself
	public function add($object, $metadata = null)
	{
		// get an array of metadata from somewhere
		if ($metadata == null)
			$metadata = (array)$object;
		else
			$metadata = (array)$metadata;

		// convert metadata into canonical tags
		$tags = self::tokenise( implode(' ',$metadata));

		// insert the object into the index
		$this->insert($object,$tags);
	}

	// search with an arbitrary string
	public function search($string)
	{
		$keywords = self::tokenise($string);
		return $this->query($keywords);
	}

	// convert a search string, object, or array into a series of canonical
	// tags
	public static function tokenise($string)
	{
		// force a string
		//$string = implode(' ',(array)$mess);

		// split by any kind of common delimitation
		//$tags = preg_split('/[\s\-,()_\+\.]+/i',$string);
		// split by not-an-alphanumber
		$tags = preg_split('/[^a-z0-9]+/i',$string);

		// normalise each tag
		foreach ($tags as $key => &$tag)
		{
			$tag = strtolower($tag);
			if (!$tag)
				unset($tags[$key]);
		}

		// filter duplicates
		$tags = array_unique($tags);

		// TODO: sort by length desc? --  speeds up search

		// convert to numeric array (sequential)
		return array_values($tags);
	}

	// standardise a tag (lower case alphanumeric)
	public static function normalise(&$tag)
	{
		//$tag = trim($tag);
		//$tag = trim($tag,'()[]{}\'"!£<>|#~$?%^&*');
		$tag = strtolower($tag);
		// remove non alphanumerical chars
		$tag = preg_replace('/[^a-z0-9]/i',null,$tag);
	}
}
